using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using UnitShared;

namespace tactics
{
    public class SpriteHelper
    {
        VertexPositionTexture[] sprite;
        VertexPositionTexture[] playerDecal;

        VertexDeclaration vertDecl;
        BasicEffect effect;

        #region Properties

        public VertexDeclaration VertDec
        {
            get
            {
                return vertDecl;
            }
        }

        #endregion

        public SpriteHelper(float scale)
        {
            float x, y, z;
            x = -0.5f * scale;
            y = 0.0f;
            z = 0.0f;

            sprite = new VertexPositionTexture[4];
            sprite[0] = new VertexPositionTexture(new Vector3(x, y, z), new Vector2(1, 1));
            sprite[1] = new VertexPositionTexture(new Vector3(x, y + scale, z), new Vector2(1, 0));
            sprite[2] = new VertexPositionTexture(new Vector3(x + scale, y, z), new Vector2(0, 1));
            sprite[3] = new VertexPositionTexture(new Vector3(x + scale, y + scale, z), new Vector2(0, 0));

            playerDecal = new VertexPositionTexture[4];

            vertDecl = new VertexDeclaration(TacticsEngine.Instance.GraphicsDevice, VertexPositionTexture.VertexElements);           
        }

        public void Draw(List<Player> players, TerrainMap terrainMap, TacticsCamera camera)
        {
            Vector3 playerColour = new Vector3();
            float scale = terrainMap.Scale;
            float scale2 = scale / 2;
            
            Begin();

            foreach (Player player in players)
            {
                foreach (Unit unit in player.Units)
                {
                    int uX = unit.X;
                    int uY = unit.Y;

                    float x = uX * scale + scale2;
                    float y = terrainMap.GetHeight(uX, uY);
                    float z = uY * scale + scale2;

                    // get avg height of neighbouring verts
                    float y1 = terrainMap.GetHeight(uX, uY) * scale;
                    float y2 = terrainMap.GetHeight(uX + 1, uY) * scale;
                    float y3 = terrainMap.GetHeight(uX, uY + 1) * scale;
                    float y4 = terrainMap.GetHeight(uX + 1, uY + 1) * scale;
                    float ay = terrainMap.GetAverageHeight(uX, uY);

                    playerDecal[0] = new VertexPositionTexture(new Vector3(x + scale - scale2, y2 + 0.02f, z - scale2), new Vector2(0, 1));
                    playerDecal[1] = new VertexPositionTexture(new Vector3(x - scale2, y1 + 0.02f, z - scale2), new Vector2(1, 1));
                    playerDecal[2] = new VertexPositionTexture(new Vector3(x + scale - scale2, y4 + 0.02f, z + scale - scale2), new Vector2(0, 0));
                    playerDecal[3] = new VertexPositionTexture(new Vector3(x - scale2, y3 + 0.02f, z + scale - scale2), new Vector2(1, 0));

                    if (unit.Moved && unit.Acted)
                    {
                        playerColour.X = 1.0f;
                        playerColour.Y = 1.0f;
                        playerColour.Z = 1.0f;
                    }
                    else
                    {
                        playerColour.X = player.Colour.R;
                        playerColour.Y = player.Colour.G;
                        playerColour.Z = player.Colour.B;
                    }

                    Draw(unit.worldPosition, unit.Texture, camera, playerColour);
                }
            }

            End();
        }

        public void Begin()
        {
            GraphicsDevice graphics = TacticsEngine.Instance.GraphicsDevice;

            graphics.VertexDeclaration = vertDecl;
            
            effect = new BasicEffect(graphics, null);

            effect.View = TacticsEngine.Instance.Game.Camera.CameraView;
            effect.Projection = TacticsEngine.Instance.Game.Camera.CameraProjection;
            effect.TextureEnabled = true;
           
            effect.Begin();
        }

        private void Draw(Vector3 loc, string name, TacticsCamera camera, Vector3 playerColour)
        {
            GraphicsDevice graphics = TacticsEngine.Instance.GraphicsDevice;

            Matrix cbillboard = Matrix.CreateConstrainedBillboard(loc, camera.CameraPosition, Vector3.Up, camera.CameraLookAt, null);
            Matrix billboard = Matrix.CreateBillboard(loc, camera.CameraPosition, Vector3.Up, camera.CameraLookAt);

            effect.Texture = TacticsEngine.Instance.Content.Load<Texture2D>(@"target");
            effect.World = Matrix.Identity;

            effect.DiffuseColor = playerColour;

            graphics.RenderState.DepthBufferEnable = true;
            graphics.RenderState.DestinationBlend = Blend.SourceAlpha;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                graphics.DrawUserPrimitives(PrimitiveType.TriangleStrip, playerDecal, 0, 2);
                pass.End();
            }
            
            effect.World = billboard + cbillboard;
            effect.Texture = TacticsEngine.Instance.Content.Load<Texture2D>(name);
            effect.DiffuseColor = new Vector3(1f, 1f, 1f);

            graphics.RenderState.DepthBufferEnable = true;
            graphics.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                graphics.DrawUserPrimitives(PrimitiveType.TriangleStrip, sprite, 0, 2);
                pass.End();
            }

        }

        public void End()
        {
            effect.End();
        }
    }
}